package com.zlc.common.util.wrapper;

import com.zlc.common.common.exception.BusinessException;
import com.zlc.common.util.wrapper.lambda.LambdaSett;
import com.zlc.common.util.wrapper.lambda.SFunction;
import com.zlc.common.util.wrapper.lambda.SerializedLambda;
import com.zlc.common.util.wrapper.utils.LambdaUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 集合操作工具 使劲造
 */
public final class CollectionHelper {

    private CollectionHelper(){}


    public static <T> List<T> distinct(List<T> list, SFunction<T,?> function){
         return distinct(list, Collections.singletonList(function));
    }


    public static <T> List<T> distinct(List<T> list, SFunction<T, ?>... functions){
        return distinct(list,Arrays.asList(functions));
    }

    /**
     * 集合对象去重，可传多个去重字段
     * @param list        collection
     * @param functions   field
     * @param <T>         type
     * @return            list
     */
    public static <T> List<T> distinct(List<T> list,List<SFunction<T,?>> functions){
        Map<String,T> map = new HashMap<>();
        for(T t : list){
            String key = getTVal(t,functions);
            map.put(key,t);
        }
        return new ArrayList<>(map.values());
    }


    /**
     * 查询第一个有的，第二个没有的返回
     */
    public static List<String> getNotExit(List<String> first,List<String> second){
        List<String> noExits = new ArrayList<>();
        for(String f : first){
           if(!second.contains(f)){
               noExits.add(f);
           }
        }
        return noExits;
    }

    /**
     * 移除第一个集合有的第二个集合也有的数据，保留第一个集合的新数据
     */
    public static <T>List<T> removeExit(List<T> firsts,List<T> seconds,SFunction<T,?> function){
        List<T> noExits = new ArrayList<>();
        List<String> secondValList = new ArrayList<>();
        for(T second : seconds){
            String secondVal = getTVal(second, function);
            secondValList.add(secondVal);
        }
        for(T first : firsts){
            String firstVal = getTVal(first, function);
            if(!secondValList.contains(firstVal)){
                noExits.add(first);
            }
        }
        return noExits;
    }

    public static <T>List<T> getRectIntList(List<T> one,List<T> two,SFunction<T,?> function){
        return getRectIntList(one,two,Collections.singletonList(function));
    }


    public static <T>List<T> getRectIntList(List<T> one,List<T> two,SFunction<T,?> ... functions){
        return getRectIntList(one,two,Arrays.asList(functions));
    }

    /**
     * 取交集
     */
    public static <T>List<T> getRectIntList(List<T> one,List<T> two,List<SFunction<T,?>> functions){
        Set<T> result = new HashSet<>();
        for(T first : one){
            String oneB = getTVal(first,functions);
            for(T second : two){
                String twoB = getTVal(second,functions);
                if(twoB.equals(oneB)){
                    result.add(first);
                }
            }
        }
        return new ArrayList<>(result);
    }

    @Getter
    @AllArgsConstructor
    public enum SortEnum{
        ASC,
        DESC,
    }

    @Data
    @Accessors(chain = true)
    public static class SortEntity<T>{

        private SortEnum sortEnum;

        private SFunction<T,?> function;

        private SortEntity<T> next;
    }

    @SuppressWarnings("unchecked")
    public static <T> List<T> sortList(List<T> list,SFunction<T,?> function,SortEnum sortEnum){
        SortEntity<T> one = new SortEntity<T>().setFunction(function).setSortEnum(sortEnum);
        return sortList(list,one);
    }

    @SuppressWarnings("unchecked")
    public static <T> List<T> sortList(List<T> list,SFunction<T,?> function,SortEnum sortEnum,SFunction<T,?> function1,SortEnum sortEnum1){
        SortEntity<T> one = new SortEntity<T>().setFunction(function).setSortEnum(sortEnum);
        SortEntity<T> two = new SortEntity<T>().setFunction(function1).setSortEnum(sortEnum1);
        return sortList(list,one,two);
    }


    /**
     * 集合排序，可多个字段排序
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> sortList(List<T> list,SortEntity<T> ... sortEntities){
        if(sortEntities.length == 0 || ObjectUtils.isEmpty(list)){
            return list;
        }
        SortEntity<T> sort = sortEntities[0];
        initSortEnum(sort,0,sortEntities);
        //分组
        Map<String,Object> map = new HashMap<>();
        map = sortMap(map,list,sort);
        //拆组
        List<T> result = new LinkedList<>();
        decSortMap(map,result);
        return result;
    }

    public static Map<?,?> sortMap(Map<?,?> map,SortEnum sortEnum){
        return map.entrySet().stream().sorted(Map.Entry.comparingByKey((s1,s2)->{
            if(SortEnum.ASC.equals(sortEnum)) {
                if (s1 instanceof String) {
                    return ((String) s1).compareTo((String) s2);
                }else if(s1 instanceof Integer){
                    return ((Integer) s1).compareTo((Integer) s2);
                }else if(s1 instanceof Long){
                    return ((Long) s1).compareTo((Long) s2);
                }else if(s1 instanceof Double){
                    return ((Double) s1).compareTo((Double) s2);
                }else if(s1 instanceof Float){
                    return ((Float) s1).compareTo((Float) s2);
                }else if(s1 instanceof Short){
                    return ((Short) s1).compareTo((Short) s2);
                }else if(s1 instanceof Date){
                    return ((Date) s1).compareTo((Date) s2);
                }else if(s1 instanceof LocalDateTime){
                    return ((LocalDateTime) s1).compareTo((ChronoLocalDateTime<?>) s2);
                }else if(s1 instanceof LocalDate){
                    return ((LocalDate) s1).compareTo((ChronoLocalDate) s2);
                }
            } else if (SortEnum.DESC.equals(sortEnum)) {
                if (s1 instanceof String) {
                    return ((String) s2).compareTo((String) s1);
                }else if(s1 instanceof Integer){
                    return ((Integer) s2).compareTo((Integer) s1);
                }else if(s1 instanceof Long){
                    return ((Long) s2).compareTo((Long) s1);
                }else if(s1 instanceof Double){
                    return ((Double) s2).compareTo((Double) s1);
                }else if(s1 instanceof Float){
                    return ((Float) s2).compareTo((Float) s1);
                }else if(s1 instanceof Short){
                    return ((Short) s2).compareTo((Short) s1);
                }else if(s1 instanceof Date){
                    return ((Date) s2).compareTo((Date) s1);
                }else if(s1 instanceof LocalDateTime){
                    return ((LocalDateTime) s2).compareTo((ChronoLocalDateTime<?>) s1);
                }else if(s1 instanceof LocalDate){
                    return ((LocalDate) s2).compareTo((ChronoLocalDate) s1);
                }
            }
            return 1;
        })).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1, LinkedHashMap::new));
    }



    /**
     * 分组
     */
    public static <T>Map<?,?> groupList(List<T> list,SFunction<T,?> ...function){
        if(function.length == 0 || ObjectUtils.isEmpty(list)){
            return new HashMap<>();
        }
        List<SortEntity<T>> entities = new ArrayList<>();
        for(SFunction<T,?> f : function){
            SortEntity<T> entity = new SortEntity<T>();
            entity.setFunction(f);
            entity.setSortEnum(SortEnum.ASC);
            entities.add(entity);
        }
        SortEntity<T> sort = entities.get(0);
        initSortEnum(sort,0,entities.toArray(new SortEntity[]{}));
        //分组
        Map<String,Object> map = new HashMap<>();
        return sortMap(map,list,sort);
    }


    @SuppressWarnings("unchecked")
    private static <T>void decSortMap(Map<String,Object> map,List<T> list){
        for(Map.Entry<String,Object> m : map.entrySet()){
            Object value = m.getValue();
            if(value instanceof List){
                list.addAll((List<T>)value);
            }else{
                Map childMap = (Map) value;
                decSortMap(childMap,list);
            }
        }
    }


    public static <T>List<T> asList(T ... ts){
        return new ArrayList<>(Arrays.asList(ts));
    }


    @SuppressWarnings("unchecked")
    private static <T>void initSortEnum(SortEntity<T> sort,int index,SortEntity<T> ... sortEntities){
        index = index + 1;
        if(sortEntities.length > index) {
            sort.setNext(sortEntities[index]);
            initSortEnum(sortEntities[index], index, sortEntities);
        }
    }


    @SuppressWarnings("unchecked")
    private static <T>Map<String,Object> sortMap(Map<String,Object> map,List<T> list,SortEntity<T> sortEntity){
        if(sortEntity == null || ObjectUtils.isEmpty(list)){
            return map;
        }
        SFunction<T, ?> function = sortEntity.getFunction();
        for(T t : list){
            String key = getTVal(t,function);
            Object child = map.get(key);
            if(child == null){
                List<T> childT = new ArrayList<>();
                childT.add(t);
                map.put(key,childT);
            }else{
                List<T> childT = (List<T>)((Map<?, ?>)map).get(key);
                childT.add(t);
            }
        }
        SortEnum sortEnum = sortEntity.getSortEnum();
        if(sortEnum.equals(SortEnum.ASC)){
            map = map.entrySet().stream().sorted(Map.Entry.comparingByKey(Comparator.naturalOrder()))
                    .collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,(v1,v2)->v1,LinkedHashMap::new));
        } else if (sortEnum.equals(SortEnum.DESC)) {
            map = map.entrySet().stream().sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
                    .collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,(v1,v2)->v1,LinkedHashMap::new));
        }
        for(Map.Entry<String,Object> m : map.entrySet()){
            List<T> childList = (List<T>)m.getValue();
            Map<String,Object> childMap = new HashMap<>();
            if(!ObjectUtils.isEmpty(childList) && childList.size() > 1) {
                childMap = sortMap(childMap, childList, sortEntity.next);
            }
            if(!ObjectUtils.isEmpty(childMap)) {
                map.put(m.getKey(), childMap);
            }
        }
        return map;
    }


    private static <T>String getTVal(T t , SFunction<T,?> function){
        StringBuilder val = new StringBuilder();
        String s = fieldLambda2FieldName(function);
        List<Field> fields = getFields(t.getClass(),new ArrayList<>());
        for(Field field : fields){
            if(field.getName().equals(s)){
                field.setAccessible(true);
                try {
                    Object o = field.get(t);
                    val.append(String.valueOf(o));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return val.toString();
    }


    private static <T>String  getTVal(T t,List<SFunction<T,?>> functions){
        StringBuilder val = new StringBuilder();
        for(SFunction<T,?> function : functions){
            String s = fieldLambda2FieldName(function);
            List<Field> fields = getFields(t.getClass(),new ArrayList<>());
            for(Field field : fields){
                if(field.getName().equals(s)){
                    field.setAccessible(true);
                    try {
                        Object o = field.get(t);
                        val.append(String.valueOf(o)).append(":");
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return val.toString();
    }


    private static <T>Number getTValNumber(T t , SFunction<T,?> function){
        String s = fieldLambda2FieldName(function);
        List<Field> fields = getFields(t.getClass(),new ArrayList<>());
        for(Field field : fields){
            if(field.getName().equals(s)){
                field.setAccessible(true);
                try {
                    Object o = field.get(t);
                    return(Number) o;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 调整集合某元素的顺序
     * @param list   集合
     * @param orderField  排序属性
     * @param keyField   对象主键属性
     * @param old  旧对象
     * @param now  新对象
     * @param <V>  对象泛型
     * @return 新的集合
     */
    public static <V> List<V> sortChange(List<V> list,SFunction<V, Number> orderField,SFunction<V, ?> keyField,V old,V now){
        //排序属性名称
        Number oldOrder = getTValNumber(old, orderField);
        Number nowOrder = getTValNumber(now,orderField);
        if(oldOrder == null || nowOrder == null || oldOrder.equals(nowOrder)){
            return list;
        }
        int nOrder = nowOrder.intValue();
        int oOrder = oldOrder.intValue();
        SortEnum sortEnum = nOrder > oOrder ? SortEnum.ASC : SortEnum.DESC;
        List<V> sortList = sortList(list, orderField, sortEnum);
        boolean change = false;
        for(V v : sortList){
            String vKey = getTVal(v, keyField);
            Number vNumber = getTValNumber(v,orderField);
            if(vNumber == null){
                throw new BusinessException("排序字段值为空，对象"+vKey);
            }
            int vOrder = vNumber.intValue();
            String nKey = getTVal(now,keyField);
            String oKey = getTVal(old,keyField);
            if(oKey.equals(vKey)){
                setFieldNumber(v,orderField,nOrder);
            }
            if(nOrder > oOrder){
                //从上向下移动
                if(change){
                    setFieldNumber(v,orderField,vOrder-1);
                    if(vKey.equals(nKey)){
                        change = false;
                    }
                    continue;
                }
                if(vKey.equals(oKey)){
                    change = true;
                }
            }
            else{
                //从下向上移动
                if(change){
                    setFieldNumber(v,orderField,vOrder+1);
                    if(vKey.equals(nKey)){
                        change = false;
                    }
                    continue;
                }
                if(vKey.equals(oKey)){
                    change = true;
                }
            }
        }
        return sortList(sortList, orderField, SortEnum.ASC);
    }

    public static <V>void setFieldNumber(V v,SFunction<V, ?> field,Integer o){
        String s = fieldLambda2FieldName(field);
        List<Field> fields = getFields(v.getClass(),new ArrayList<>());
        for(Field f : fields){
            if(f.getName().equals(s)){
                f.setAccessible(true);
                try {
                    Class<?> type = f.getType();
                    if(type.isAssignableFrom(Integer.class)){
                        f.set(v,o);
                    }else if(type.isAssignableFrom(Long.class)){
                        f.set(v,o.longValue());
                    } else if (type.isAssignableFrom(BigDecimal.class)) {
                        f.set(v,BigDecimal.valueOf(o));
                    }else{
                        f.set(v,o);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * lambda转字段名
     *
     * @param fieldLambda
     * @return
     */
    private static  <T>String fieldLambda2FieldName(SFunction<T, ?> fieldLambda) {
        return getLambdaSett(fieldLambda).getFiledName();
    }

    /**
     * 根据fieldLambda获取 LambdaSett
     *
     * @param fieldLambda 过滤字段get方法的
     * @return
     */
    private static <T> LambdaSett getLambdaSett(SFunction<T, ?> fieldLambda) {
        SerializedLambda lambda = LambdaUtils.resolve(fieldLambda);
        String filedName = resolveFieldName(lambda.getImplMethodName());
        String key = lambda.getImplClass().getName() + filedName;
        return new LambdaSett(lambda, filedName);
    }


    /**
     * 方法名转字段名
     */
    private static String resolveFieldName(String methodName) {
        methodName = methodName.replace("get", "");
        return (methodName.charAt(0) + "").toLowerCase() + methodName.substring(1);
    }

    public static List<String> toList(String ...o){
        return new ArrayList<>(Arrays.asList(o));
    }



    private static <T>List<Field>  getFields(Class<T> copy, List<Field> fields){
        fields.addAll(Arrays.asList(copy.getDeclaredFields()));
        if(copy.getSuperclass() != Object.class){
            Class<?> superCopy = copy.getSuperclass();
            getFields(superCopy,fields);
            return fields;
        }else{
            return fields;
        }
    }

    @Data
    @Accessors(chain = true)
    public static class Student{

        private Integer grade;

        private String name;

        private Integer score;
    }


}
